/**
  ******************************************************************************
  * @author     Chris
  * @since      2023/9/29 22:38
  *
  * @file       hd_oled.c
  * @brief      OLED Hardware Driver.
  *
  * @note       This file contains the hardware driver for the OLED.
  *
  * @warning    None.
  ******************************************************************************
  * Change Logs:
  *   Date           Author       Notes
  *   2023-09-29     Chris        the first version
  *
  ******************************************************************************
  */

#ifndef USE_FULL_OLED

#include "stm32g4xx.h"
#include "stm32g4xx_ll_gpio.h"
#include "delay.h"
#include "hd_oled.h"
#include "pd_h_gpio.h"


/** region Declaration and Definition Region */

/** region Variables and Constants Region */
/** region OLED 初始化指令数组 */
const uint8_t OLED_INIT_CMD[] = {
        0xAE, // 关闭显示
        0xD5, // 设置时钟分频因子,震荡频率
        0x80, // [3:0],分频因子;[7:4],震荡频率
        0xA8, // 设置驱动路数
        0X3F, // 默认0X3F(1/64)
        0xD3, // 设置显示偏移
        0X00, // 默认为0
        0x40, // 设置显示开始行 [5:0],行数.
        0x8D, // 电荷泵设置
        0x14, // bit2，开启/关闭
        0x20, // 设置内存地址模式
        0x02, // [1:0],00，列地址模式;01，行地址模式;10,页地址模式;默认10;
        0xA1, // 段重定义设置,bit0:0,0->0;1,0->127;
        0xC8, // 设置COM扫描方向;bit3:0,普通模式;1,重定义模式 COM[N-1]->COM0;N:驱动路数
        0xDA, // 设置COM硬件引脚配置
        0x12, // [5:4]配置
        0x81, // 对比度设置
        0xEF, // 1~255;默认0X7F (亮度设置,越大越亮)
        0xD9, // 设置预充电周期
        0xf1, // [3:0],PHASE 1;[7:4],PHASE 2;
        0xDB, // 设置VCOMH 电压倍率
        0x30, // [6:4] 000,0.65*vcc;001,0.77*vcc;011,0.83*vcc;
        0xA4, // 全局显示开启;bit0:1,开启;0,关闭;(白屏/黑屏)
        0xA6, // 设置显示方式;bit0:1,反相显示;0,正常显示
        0xAF, // 开启显示
};

/** endregion */

uint8_t OLED_FONT_MODE = OLED_MODE_POSITIVE;    // 缺省显示模式
uint8_t OLED_FONT_SIZE = OLED_SIZE_16;    // 固定字体大小

/**
 * 定义 OLED 操作引脚
 */
#define OLED_SCK(x)     OLED_WriteBit(GPIOA, LL_GPIO_PIN_9, x)
#define OLED_SDA(x)     OLED_WriteBit(GPIOA, LL_GPIO_PIN_8, x)
#if OLED_PROTOCOL == 1      // SPI
#define OLED_CS(x)      OLED_WriteBit(GPIOB, LL_GPIO_PIN_12, x)
#define OLED_RS(x)      OLED_WriteBit(GPIOB, LL_GPIO_PIN_13, x)
#define OLED_RST(x)     OLED_WriteBit(GPIOG, LL_GPIO_PIN_10, x)
#elif OLED_PROTOCOL == 2    // 8080
#define OLED_DATA(x)    OLED_WriteBit(GPIOB, LL_GPIO_PIN_12, x)
#endif
/** endregion Variables and Constants Region */

/** region Function Declaration Region */
void OLED_SetCursor(uint8_t y, uint8_t x);

void OLED_WriteByte(uint8_t dat, uint8_t write_type);

void OLED_WriteBit(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin, FlagStatus BitVal);

uint32_t HD_OLED_Pow(uint32_t x, uint32_t y);

uint8_t HD_OLED_GetChar(uint8_t size, uint8_t x, uint8_t y);
/** endregion Function Declaration Region */


/** endregion Declaration and Definition Region */


/** region public function region */

/**
  * @brief  OLED 初始化
  *
  * @note   初始化：
  *         1. GPIO 引脚初始化
  *         2. 通信引脚设置初始化值
  *         3. OLED 初始化指令
  * @param  size 固定为 16x8
  * @param  mode 显示模式，即阳显或阴显
  * @retval 无
  */
void HD_OLED_Init(uint8_t size, uint8_t mode) {
    OLED_FONT_SIZE = size == 16 ? size : 16;
    OLED_FONT_MODE = mode;

    PD_H_GPIO_OLEDInit(OLED_PROTOCOL);

    // OLED 初始化配置
    for (int i = 0; i < sizeof(OLED_INIT_CMD); ++i) {
        OLED_WriteByte(OLED_INIT_CMD[i], 0);
    }

    HD_OLED_Clear();
}

void HD_OLED_SetSize(uint8_t size) {
    OLED_FONT_SIZE = size == 16 ? size : 16;
}

void HD_OLED_SetMode(uint8_t mode) {
    OLED_FONT_MODE = mode;
}

/**
  * @brief  OLED 清屏
  *
  * @note   阳显模式下，全屏熄灭；阴显模式下，全屏点亮。
  * @param  void
  * @retval 无
  */
void HD_OLED_Clear(void) {
    uint8_t i, j;
    for (j = 0; j < 8; j++) {
        OLED_SetCursor(j, 0);
        for (i = 0; i < 128; i++) {
            OLED_WriteByte(OLED_FONT_MODE == OLED_MODE_POSITIVE ? 0x00 : 0xFF, 1);
        }
    }
}

/**
  * @brief  OLED 全屏显示
  *
  * @note   阳显模式下，全屏点亮；阴显模式下，全屏熄灭。
  * @param  void
  * @retval 无
  */
void HD_OLED_Full(void) {
    uint8_t i, j;
    for (j = 0; j < 8; j++) {
        OLED_SetCursor(j, 0);
        for (i = 0; i < 128; i++) {
            OLED_WriteByte(OLED_FONT_MODE == OLED_MODE_POSITIVE ? 0xFF : 0x00, 1);
        }
    }
}

/**
  * @brief  OLED 显示一个字符
  *
  * @note   显示 ASCII 字符
  * @param  x 坐标，范围：0~63
  * @param  y 坐标，范围：0~127
  * @param  chr 要显示的一个字符，范围：ASCII可见字符
  * @retval 无
  */
void HD_OLED_ShowChar(uint8_t x, uint8_t y, char chr) {
    uint8_t Line = y / 16 + 1;
    uint8_t Column = x / 8 + 1;

    OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8);           // 设置光标位置在上半部分
    for (uint8_t i = 0; i < OLED_FONT_SIZE / 2; i++) {
        OLED_WriteByte(HD_OLED_GetChar(OLED_FONT_SIZE, chr - ' ', i), 1);     // 显示上半部分内容
    }

    OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8);       // 设置光标位置在下半部分
    for (uint8_t i = 0; i < OLED_FONT_SIZE / 2; i++) {
        OLED_WriteByte(HD_OLED_GetChar(OLED_FONT_SIZE, chr - ' ', i + 8), 1);  // 显示下半部分内容
    }
}

/**
  * @brief  OLED 显示字符串
  *
  * @note   通过 @ref HD_OLED_ShowChar() 函数逐个显示
  * @param  x 坐标，范围：0~63
  * @param  y 坐标，范围：0~127
  * @param  str 要显示的字符串，范围：ASCII可见字符
  * @retval 无
  */
void HD_OLED_ShowString(uint8_t x, uint8_t y, char *str) {
    for (uint8_t i = 0; str[i] != '\0'; i++) {
        HD_OLED_ShowChar(x + i * 8, y, str[i]);
    }
}

/**
  * @brief  OLED 显示数字（十进制，正数）
  *
  * @note   将数字逐个取出，通过 @ref HD_OLED_ShowChar() 显示
  * @param  x 坐标，范围：0~63
  * @param  y 坐标，范围：0~127
  * @param  num 要显示的数字，范围：0~4294967295
  * @param  len 要显示数字的长度，范围：1~10
  * @retval 无
  */
void HD_OLED_ShowNum(uint8_t x, uint8_t y, uint32_t num, uint8_t len) {
    uint8_t i;
    for (i = 0; i < len; i++) {
        HD_OLED_ShowChar(x + i * 8, y, num / HD_OLED_Pow(10, len - i - 1) % 10 + '0');
    }
}

/**
  * @brief  OLED 显示数字（十进制，带符号数）
  *
  * @note   先显示符号，然后逐个取出数字后通过 @ref HD_OLED_ShowChar 显示
  * @param  x 坐标，范围：0~63
  * @param  y 坐标，范围：0~127
  * @param  num 要显示的数字，范围：-2147483648~2147483647
  * @param  len 要显示数字的长度，范围：1~10
  * @retval 无
  */
void HD_OLED_ShowSignedNum(uint8_t x, uint8_t y, int32_t num, uint8_t len) {
    uint8_t i;
    uint32_t Number1;
    if (num >= 0) {
        HD_OLED_ShowChar(x, y, '+');
        Number1 = num;
    } else {
        HD_OLED_ShowChar(x, y, '-');
        Number1 = -num;
    }
    for (i = 0; i < len; i++) {
        HD_OLED_ShowChar(x + i * 8 + 8, y, Number1 / HD_OLED_Pow(10, len - i - 1) % 10 + '0');
    }
}

/**
  * @brief  OLED显示数字（十六进制，正数）
  *
  * @note   逐个取出数字，转换为十六进制后，通过 @ref HD_OLED_ShowChar 显示
  * @param  x 坐标，范围：0~63
  * @param  y 坐标，范围：0~127
  * @param  num 要显示的数字，范围：0~0xFFFFFFFF
  * @param  len 要显示数字的长度，范围：1~8
  * @retval 无
  */
void HD_OLED_ShowHexNum(uint8_t x, uint8_t y, uint32_t num, uint8_t len) {
    uint8_t i, SingleNumber;
    for (i = 0; i < len; i++) {
        SingleNumber = num / HD_OLED_Pow(16, len - i - 1) % 16;
        if (SingleNumber < 10) {
            HD_OLED_ShowChar(x + i * 8, y, SingleNumber + '0');
        } else {
            HD_OLED_ShowChar(x + i * 8, y, SingleNumber - 10 + 'A');
        }
    }
}

/**
  * @brief  OLED显示数字（二进制，正数）
  *
  * @note   逐个取出二进制的位，通过 @ref HD_OLED_ShowChar() 函数显示
  * @param  x 坐标，范围：0~63
  * @param  y 坐标，范围：0~127
  * @param  num 要显示的数字，范围：0~1111 1111 1111 1111
  * @param  len 要显示数字的长度，范围：1~16
  * @retval 无
  */
void HD_OLED_ShowBinNum(uint8_t x, uint8_t y, uint32_t num, uint8_t len) {
    uint8_t i;
    for (i = 0; i < len; i++) {
        HD_OLED_ShowChar(x + i * 8, y, num / HD_OLED_Pow(2, len - i - 1) % 2 + '0');
    }
}

/**
 * @brief   OLED显示浮点数
 *
 * @note    无
 * @param   x   起始横坐标
 * @param   y   起始纵坐标
 * @param   num 待显示的浮点数
 * @param   len 待显示的浮点数长度
 * @retval  无
 */
void HD_OLED_ShowFloatNum(uint16_t x, uint16_t y, float num, uint8_t len) {
    const uint16_t numOfHundred = (uint16_t) (num * 100);
    for (uint8_t i = 0; i < len; i++) {
        uint8_t temp = (numOfHundred / HD_OLED_Pow(10, len - i - 1)) % 10;
        if (i == (len - 2)) {
            // 显示小数点
            HD_OLED_ShowChar(x + (len - 2) * OLED_FONT_SIZE / 2, y, '.');
            i++;
            len += 1;
        }

        HD_OLED_ShowChar(x + i * OLED_FONT_SIZE / 2, y, temp + '0');
    }
}

void HD_OLED_RefreshGram(void) {}

/** endregion public function region */


/** region private function region */

/**
  * @brief  OLED设置光标位置
  *
  * @note
  * @param  y 以左上角为原点，向下方向的坐标，范围：0~7
  * @param  x 以左上角为原点，向右方向的坐标，范围：0~127
  * @retval 无
  */
void OLED_SetCursor(uint8_t y, uint8_t x) {
    OLED_WriteByte(0xB0 | y, 0);                    // 设置 Y 位置
    OLED_WriteByte(0x10 | ((x & 0xF0) >> 4), 0);    // 设置 X 位置高 4 位
    OLED_WriteByte(0x00 | (x & 0x0F), 0);           // 设置 X 位置低 4 位
}


#if OLED_PROTOCOL == 1      // SPI 通信

/**
 * @brief OLED 写
 *
 * @note   IO 口 模拟 SPI 写
 * @param dat         要写入的数据或命令
 * @param write_type  数据/命令标志：0，表示命令；1，表示数据
 * @retval 无
 */
void OLED_WriteByte(uint8_t dat, uint8_t write_type) {
    dat = (OLED_FONT_MODE == OLED_MODE_NEGATIVE ? (dat ^ 0) : dat);
    OLED_RS((FlagStatus)write_type);
    OLED_CS(RESET);

    for (int i = 0; i < 8; ++i) {
        OLED_SCK(RESET);
        if (dat & 0x80) { OLED_SDA(SET); }
        else {
            OLED_SDA(RESET);
        }
        OLED_SCK(SET);
        dat <<= 1;
    }

    OLED_CS(SET);
}

/**
 * @brief 设置/清零 GPIO 位
 *
 * @note
 * @param GPIOx @ref GPIO_TypeDef
 * @param GPIO_Pin GPIO 引脚
 * @param BitVal 要设置的 GPIO 值
 */
void OLED_WriteBit(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin, FlagStatus BitVal) {
    PD_H_GPIO_WriteBit(GPIOx, GPIO_Pin, BitVal);
}

#elif OLED_PROTOCOL == 3    // I2C 通信

/**
  * @brief  I2C开始
  * @param  无
  * @retval 无
  */
void OLED_I2C_Start(void) {
    OLED_SDA(1);
    OLED_SCK(1);
    OLED_SDA(0);
    OLED_SCK(0);
}

/**
  * @brief  I2C停止
  * @param  无
  * @retval 无
  */
void OLED_I2C_Stop(void) {
    OLED_SDA(0);
    OLED_SCK(1);
    OLED_SDA(1);
}

/**
  * @brief  I2C发送一个字节
  * @param  Byte 要发送的一个字节
  * @retval 无
  */
void OLED_I2C_SendByte(uint8_t Byte) {
    uint8_t i;
    for (i = 0; i < 8; i++) {
        OLED_SDA(Byte & (0x80 >> i));
        OLED_SCK(1);
        OLED_SCK(0);
    }
    OLED_SCK(1);    //额外的一个时钟，不处理应答信号
    OLED_SCK(0);
}

/**
 * @brief OLED 写
 *
 * @note   IO 口 模拟 I2C 写
 * @param dat         要写入的数据或命令
 * @param write_type  数据/命令标志：0，表示命令；1，表示数据
 * @retval 无
 */
void OLED_WriteByte(uint8_t dat, uint8_t write_type) {
    dat = (OLED_FONT_MODE == OLED_MODE_NEGATIVE ? (dat ^ 0) : dat);
    OLED_I2C_Start();
    OLED_I2C_SendByte(0x78);                            // 从机地址
    OLED_I2C_SendByte(write_type == 0 ? 0x00 : 0x40);   // 写命令
    OLED_I2C_SendByte(dat);
    OLED_I2C_Stop();
}

/**
 * @brief 设置/清零 GPIO 位
 *
 * @note
 * @param GPIOx @ref GPIO_TypeDef
 * @param GPIO_Pin GPIO 引脚
 * @param BitVal 要设置的 GPIO 值
 */
void OLED_WriteBit(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, FlagStatus BitVal) {
    delay_us(1);
    PD_H_GPIO_WriteBit(GPIOx, GPIO_Pin, BitVal);
}

#endif


/**
  * @brief  OLED次方函数
  * @param x 底数
  * @param y 指数
  * @retval 返回值等于 X 的 Y 次方
  */
uint32_t HD_OLED_Pow(uint32_t x, uint32_t y) {
    uint32_t Result = 1;
    while (y--) {
        Result *= x;
    }
    return Result;
}

/** region 16*8 ASCII 字符集点阵 */

/*OLED字模库，宽8像素，高16像素*/
const uint8_t OLED_BASE_ASC_1608[][16] = {
        {
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
        }, //  0
        {
                0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x33, 0x30, 0x00, 0x00, 0x00
        }, //! 1
        {
                0x00, 0x10, 0x0C, 0x06, 0x10, 0x0C, 0x06, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
        }, //" 2
        {
                0x40, 0xC0, 0x78, 0x40, 0xC0, 0x78, 0x40, 0x00,
                0x04, 0x3F, 0x04, 0x04, 0x3F, 0x04, 0x04, 0x00
        }, //# 3
        {
                0x00, 0x70, 0x88, 0xFC, 0x08, 0x30, 0x00, 0x00,
                0x00, 0x18, 0x20, 0xFF, 0x21, 0x1E, 0x00, 0x00
        }, //$ 4
        {
                0xF0, 0x08, 0xF0, 0x00, 0xE0, 0x18, 0x00, 0x00,
                0x00, 0x21, 0x1C, 0x03, 0x1E, 0x21, 0x1E, 0x00
        }, //% 5
        {
                0x00, 0xF0, 0x08, 0x88, 0x70, 0x00, 0x00, 0x00,
                0x1E, 0x21, 0x23, 0x24, 0x19, 0x27, 0x21, 0x10
        }, //& 6
        {
                0x10, 0x16, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
        }, //' 7
        {
                0x00, 0x00, 0x00, 0xE0, 0x18, 0x04, 0x02, 0x00,
                0x00, 0x00, 0x00, 0x07, 0x18, 0x20, 0x40, 0x00
        }, //( 8
        {
                0x00, 0x02, 0x04, 0x18, 0xE0, 0x00, 0x00, 0x00,
                0x00, 0x40, 0x20, 0x18, 0x07, 0x00, 0x00, 0x00
        }, //) 9
        {
                0x40, 0x40, 0x80, 0xF0, 0x80, 0x40, 0x40, 0x00,
                0x02, 0x02, 0x01, 0x0F, 0x01, 0x02, 0x02, 0x00
        }, //* 10
        {
                0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00,
                0x01, 0x01, 0x01, 0x1F, 0x01, 0x01, 0x01, 0x00
        }, //+ 11
        {
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x80, 0xB0, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00
        }, //, 12
        {
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
        }, //- 13
        {
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00
        }, //. 14
        {
                0x00, 0x00, 0x00, 0x00, 0x80, 0x60, 0x18, 0x04,
                0x00, 0x60, 0x18, 0x06, 0x01, 0x00, 0x00, 0x00
        }, /// 15
        {
                0x00, 0xE0, 0x10, 0x08, 0x08, 0x10, 0xE0, 0x00,
                0x00, 0x0F, 0x10, 0x20, 0x20, 0x10, 0x0F, 0x00
        }, //0 16
        {
                0x00, 0x10, 0x10, 0xF8, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00
        }, //1 17
        {
                0x00, 0x70, 0x08, 0x08, 0x08, 0x88, 0x70, 0x00,
                0x00, 0x30, 0x28, 0x24, 0x22, 0x21, 0x30, 0x00
        }, //2 18
        {
                0x00, 0x30, 0x08, 0x88, 0x88, 0x48, 0x30, 0x00,
                0x00, 0x18, 0x20, 0x20, 0x20, 0x11, 0x0E, 0x00
        }, //3 19
        {
                0x00, 0x00, 0xC0, 0x20, 0x10, 0xF8, 0x00, 0x00,
                0x00, 0x07, 0x04, 0x24, 0x24, 0x3F, 0x24, 0x00
        }, //4 20
        {
                0x00, 0xF8, 0x08, 0x88, 0x88, 0x08, 0x08, 0x00,
                0x00, 0x19, 0x21, 0x20, 0x20, 0x11, 0x0E, 0x00
        }, //5 21
        {
                0x00, 0xE0, 0x10, 0x88, 0x88, 0x18, 0x00, 0x00,
                0x00, 0x0F, 0x11, 0x20, 0x20, 0x11, 0x0E, 0x00
        }, //6 22
        {
                0x00, 0x38, 0x08, 0x08, 0xC8, 0x38, 0x08, 0x00,
                0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00
        }, //7 23
        {
                0x00, 0x70, 0x88, 0x08, 0x08, 0x88, 0x70, 0x00,
                0x00, 0x1C, 0x22, 0x21, 0x21, 0x22, 0x1C, 0x00
        }, //8 24
        {
                0x00, 0xE0, 0x10, 0x08, 0x08, 0x10, 0xE0, 0x00,
                0x00, 0x00, 0x31, 0x22, 0x22, 0x11, 0x0F, 0x00
        }, //9 25
        {
                0x00, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00
        }, //: 26
        {
                0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x80, 0x60, 0x00, 0x00, 0x00, 0x00
        }, //; 27
        {
                0x00, 0x00, 0x80, 0x40, 0x20, 0x10, 0x08, 0x00,
                0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00
        }, //< 28
        {
                0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00,
                0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00
        }, //= 29
        {
                0x00, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00, 0x00,
                0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, 0x00
        }, //> 30
        {
                0x00, 0x70, 0x48, 0x08, 0x08, 0x08, 0xF0, 0x00,
                0x00, 0x00, 0x00, 0x30, 0x36, 0x01, 0x00, 0x00
        }, //? 31
        {
                0xC0, 0x30, 0xC8, 0x28, 0xE8, 0x10, 0xE0, 0x00,
                0x07, 0x18, 0x27, 0x24, 0x23, 0x14, 0x0B, 0x00
        }, //@ 32
        {
                0x00, 0x00, 0xC0, 0x38, 0xE0, 0x00, 0x00, 0x00,
                0x20, 0x3C, 0x23, 0x02, 0x02, 0x27, 0x38, 0x20
        }, //A 33
        {
                0x08, 0xF8, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00,
                0x20, 0x3F, 0x20, 0x20, 0x20, 0x11, 0x0E, 0x00
        }, //B 34
        {
                0xC0, 0x30, 0x08, 0x08, 0x08, 0x08, 0x38, 0x00,
                0x07, 0x18, 0x20, 0x20, 0x20, 0x10, 0x08, 0x00
        }, //C 35
        {
                0x08, 0xF8, 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00,
                0x20, 0x3F, 0x20, 0x20, 0x20, 0x10, 0x0F, 0x00
        }, //D 36
        {
                0x08, 0xF8, 0x88, 0x88, 0xE8, 0x08, 0x10, 0x00,
                0x20, 0x3F, 0x20, 0x20, 0x23, 0x20, 0x18, 0x00
        }, //E 37
        {
                0x08, 0xF8, 0x88, 0x88, 0xE8, 0x08, 0x10, 0x00,
                0x20, 0x3F, 0x20, 0x00, 0x03, 0x00, 0x00, 0x00
        }, //F 38
        {
                0xC0, 0x30, 0x08, 0x08, 0x08, 0x38, 0x00, 0x00,
                0x07, 0x18, 0x20, 0x20, 0x22, 0x1E, 0x02, 0x00
        }, //G 39
        {
                0x08, 0xF8, 0x08, 0x00, 0x00, 0x08, 0xF8, 0x08,
                0x20, 0x3F, 0x21, 0x01, 0x01, 0x21, 0x3F, 0x20
        }, //H 40
        {
                0x00, 0x08, 0x08, 0xF8, 0x08, 0x08, 0x00, 0x00,
                0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00
        }, //I 41
        {
                0x00, 0x00, 0x08, 0x08, 0xF8, 0x08, 0x08, 0x00,
                0xC0, 0x80, 0x80, 0x80, 0x7F, 0x00, 0x00, 0x00
        }, //J 42
        {
                0x08, 0xF8, 0x88, 0xC0, 0x28, 0x18, 0x08, 0x00,
                0x20, 0x3F, 0x20, 0x01, 0x26, 0x38, 0x20, 0x00
        }, //K 43
        {
                0x08, 0xF8, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x20, 0x3F, 0x20, 0x20, 0x20, 0x20, 0x30, 0x00
        }, //L 44
        {
                0x08, 0xF8, 0xF8, 0x00, 0xF8, 0xF8, 0x08, 0x00,
                0x20, 0x3F, 0x00, 0x3F, 0x00, 0x3F, 0x20, 0x00
        }, //M 45
        {
                0x08, 0xF8, 0x30, 0xC0, 0x00, 0x08, 0xF8, 0x08,
                0x20, 0x3F, 0x20, 0x00, 0x07, 0x18, 0x3F, 0x00
        }, //N 46
        {
                0xE0, 0x10, 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00,
                0x0F, 0x10, 0x20, 0x20, 0x20, 0x10, 0x0F, 0x00
        }, //O 47
        {
                0x08, 0xF8, 0x08, 0x08, 0x08, 0x08, 0xF0, 0x00,
                0x20, 0x3F, 0x21, 0x01, 0x01, 0x01, 0x00, 0x00
        }, //P 48
        {
                0xE0, 0x10, 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00,
                0x0F, 0x18, 0x24, 0x24, 0x38, 0x50, 0x4F, 0x00
        }, //Q 49
        {
                0x08, 0xF8, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00,
                0x20, 0x3F, 0x20, 0x00, 0x03, 0x0C, 0x30, 0x20
        }, //R 50
        {
                0x00, 0x70, 0x88, 0x08, 0x08, 0x08, 0x38, 0x00,
                0x00, 0x38, 0x20, 0x21, 0x21, 0x22, 0x1C, 0x00
        }, //S 51
        {
                0x18, 0x08, 0x08, 0xF8, 0x08, 0x08, 0x18, 0x00,
                0x00, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x00, 0x00
        }, //T 52
        {
                0x08, 0xF8, 0x08, 0x00, 0x00, 0x08, 0xF8, 0x08,
                0x00, 0x1F, 0x20, 0x20, 0x20, 0x20, 0x1F, 0x00
        }, //U 53
        {
                0x08, 0x78, 0x88, 0x00, 0x00, 0xC8, 0x38, 0x08,
                0x00, 0x00, 0x07, 0x38, 0x0E, 0x01, 0x00, 0x00
        }, //V 54
        {
                0xF8, 0x08, 0x00, 0xF8, 0x00, 0x08, 0xF8, 0x00,
                0x03, 0x3C, 0x07, 0x00, 0x07, 0x3C, 0x03, 0x00
        }, //W 55
        {
                0x08, 0x18, 0x68, 0x80, 0x80, 0x68, 0x18, 0x08,
                0x20, 0x30, 0x2C, 0x03, 0x03, 0x2C, 0x30, 0x20
        }, //X 56
        {
                0x08, 0x38, 0xC8, 0x00, 0xC8, 0x38, 0x08, 0x00,
                0x00, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x00, 0x00
        }, //Y 57
        {
                0x10, 0x08, 0x08, 0x08, 0xC8, 0x38, 0x08, 0x00,
                0x20, 0x38, 0x26, 0x21, 0x20, 0x20, 0x18, 0x00
        }, //Z 58
        {
                0x00, 0x00, 0x00, 0xFE, 0x02, 0x02, 0x02, 0x00,
                0x00, 0x00, 0x00, 0x7F, 0x40, 0x40, 0x40, 0x00
        }, //[ 59
        {
                0x00, 0x0C, 0x30, 0xC0, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x01, 0x06, 0x38, 0xC0, 0x00
        }, //\ 60
        {
                0x00, 0x02, 0x02, 0x02, 0xFE, 0x00, 0x00, 0x00,
                0x00, 0x40, 0x40, 0x40, 0x7F, 0x00, 0x00, 0x00
        }, //] 61
        {
                0x00, 0x00, 0x04, 0x02, 0x02, 0x02, 0x04, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
        }, //^ 62
        {
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80
        }, //_ 63
        {
                0x00, 0x02, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
        }, //` 64
        {
                0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00,
                0x00, 0x19, 0x24, 0x22, 0x22, 0x22, 0x3F, 0x20
        }, //a 65
        {
                0x08, 0xF8, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00,
                0x00, 0x3F, 0x11, 0x20, 0x20, 0x11, 0x0E, 0x00
        }, //b 66
        {
                0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00,
                0x00, 0x0E, 0x11, 0x20, 0x20, 0x20, 0x11, 0x00
        }, //c 67
        {
                0x00, 0x00, 0x00, 0x80, 0x80, 0x88, 0xF8, 0x00,
                0x00, 0x0E, 0x11, 0x20, 0x20, 0x10, 0x3F, 0x20
        }, //d 68
        {
                0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00,
                0x00, 0x1F, 0x22, 0x22, 0x22, 0x22, 0x13, 0x00
        }, //e 69
        {
                0x00, 0x80, 0x80, 0xF0, 0x88, 0x88, 0x88, 0x18,
                0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00
        }, //f 70
        {
                0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00,
                0x00, 0x6B, 0x94, 0x94, 0x94, 0x93, 0x60, 0x00
        }, //g 71
        {
                0x08, 0xF8, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00,
                0x20, 0x3F, 0x21, 0x00, 0x00, 0x20, 0x3F, 0x20
        }, //h 72
        {
                0x00, 0x80, 0x98, 0x98, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00
        }, //i 73
        {
                0x00, 0x00, 0x00, 0x80, 0x98, 0x98, 0x00, 0x00,
                0x00, 0xC0, 0x80, 0x80, 0x80, 0x7F, 0x00, 0x00
        }, //j 74
        {
                0x08, 0xF8, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00,
                0x20, 0x3F, 0x24, 0x02, 0x2D, 0x30, 0x20, 0x00
        }, //k 75
        {
                0x00, 0x08, 0x08, 0xF8, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00
        }, //l 76
        {
                0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00,
                0x20, 0x3F, 0x20, 0x00, 0x3F, 0x20, 0x00, 0x3F
        }, //m 77
        {
                0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00,
                0x20, 0x3F, 0x21, 0x00, 0x00, 0x20, 0x3F, 0x20
        }, //n 78
        {
                0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00,
                0x00, 0x1F, 0x20, 0x20, 0x20, 0x20, 0x1F, 0x00
        }, //o 79
        {
                0x80, 0x80, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00,
                0x80, 0xFF, 0xA1, 0x20, 0x20, 0x11, 0x0E, 0x00
        }, //p 80
        {
                0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00,
                0x00, 0x0E, 0x11, 0x20, 0x20, 0xA0, 0xFF, 0x80
        }, //q 81
        {
                0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x00,
                0x20, 0x20, 0x3F, 0x21, 0x20, 0x00, 0x01, 0x00
        }, //r 82
        {
                0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00,
                0x00, 0x33, 0x24, 0x24, 0x24, 0x24, 0x19, 0x00
        }, //s 83
        {
                0x00, 0x80, 0x80, 0xE0, 0x80, 0x80, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x1F, 0x20, 0x20, 0x00, 0x00
        }, //t 84
        {
                0x80, 0x80, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00,
                0x00, 0x1F, 0x20, 0x20, 0x20, 0x10, 0x3F, 0x20
        }, //u 85
        {
                0x80, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x80,
                0x00, 0x01, 0x0E, 0x30, 0x08, 0x06, 0x01, 0x00
        }, //v 86
        {
                0x80, 0x80, 0x00, 0x80, 0x00, 0x80, 0x80, 0x80,
                0x0F, 0x30, 0x0C, 0x03, 0x0C, 0x30, 0x0F, 0x00
        }, //w 87
        {
                0x00, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x00,
                0x00, 0x20, 0x31, 0x2E, 0x0E, 0x31, 0x20, 0x00
        }, //x 88
        {
                0x80, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x80,
                0x80, 0x81, 0x8E, 0x70, 0x18, 0x06, 0x01, 0x00
        }, //y 89
        {
                0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00,
                0x00, 0x21, 0x30, 0x2C, 0x22, 0x21, 0x30, 0x00
        }, //z 90
        {
                0x00, 0x00, 0x00, 0x00, 0x80, 0x7C, 0x02, 0x02,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x40, 0x40
        }, //{ 91
        {
                0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00
        }, //| 92
        {
                0x00, 0x02, 0x02, 0x7C, 0x80, 0x00, 0x00, 0x00,
                0x00, 0x40, 0x40, 0x3F, 0x00, 0x00, 0x00, 0x00
        }, //} 93
        {
                0x00, 0x06, 0x01, 0x01, 0x02, 0x02, 0x04, 0x04,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
        }, //~ 94
};

/** endregion */

uint8_t HD_OLED_GetChar(uint8_t size, uint8_t x, uint8_t y) {
    switch (size) {
        case 16:
            return OLED_BASE_ASC_1608[x][y];
        case 12:
        case 24:
            return 0x00;
        default:
            return 0xFF;
    }
}

/** endregion private function region */

#endif
